﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;

namespace ManagedCli
{
    public enum Mode
    {
        Mode1, Mode2
    }
    class Program
    {
        static void Main(string[] args)
        {
            CommandLineApplication.Execute<Program>(args);
        }

        [Option(ShortName = "u")]
        public string UrlTemplate { get; set; }
        [Option(ShortName = "z")]
        public int Z { get; set; } = 3;
        [Option(ShortName = "x")]
        public int X { get; set; } = 8;
        [Option(ShortName = "y")]
        public int Y { get; set; } = 8;
        [Option(ShortName = "t")]
        public int Threads { get; set; } = 6;
        [Option(ShortName = "m")]
        public Mode Mode { get; set; } = Mode.Mode1;



        public void OnExecute()
        {
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            var urlt = string.IsNullOrEmpty(UrlTemplate) ? "https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{0}/{1}/{2}" : UrlTemplate;
            var tag = new TaskArgsGenerator(urlt, X, Y, Z);
            var tqm = new TaskQueueManager();
            for (int i = 0; i < Threads; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    lock (tag)
                    {

                        if (tag.ShouldGenerateLatestTask())
                        {
                            var singleTask = new SingleDownTask()
                            {
                                DownloadInfo = tag.GenerateLatestTask(),
                                Generator = tag
                            };
                            lock (tqm)
                            {
                                tqm.AddnewSingleDownTask(singleTask);
                            }
                        }

                    }

                });



            }
            Console.ReadLine();
            ;

        }

        public void ExecuteMode2()
        {

        }

    }



    class TaskQueueManager
    {
        public List<SingleDownTask> SingleDownTasks { get; set; } = new List<SingleDownTask>();

        public void AddnewSingleDownTask(SingleDownTask singleDownTask)
        {
            SingleDownTasks.Add(singleDownTask);
            singleDownTask.Finished += (a, b) =>
            {
                SingleDownTasks.Remove(singleDownTask);
                if (singleDownTask.Generator.ShouldGenerateLatestTask())
                {
                    var newSingleDownTask = new SingleDownTask()
                    {
                        DownloadInfo = singleDownTask.Generator.GenerateLatestTask(),
                        Generator = singleDownTask.Generator
                    };
                    AddnewSingleDownTask(newSingleDownTask);
                }
            };
            singleDownTask.DownloadStart();
        }

    }
    public class SingleDownTask
    {
        public TaskArgsGenerator Generator { get; set; }
        public int AttemptLeft { get; set; } = 5;
        public DownloadInfo DownloadInfo { get; set; }
        public void DownloadStart()
        {
            var wc = new WebClient();
            var filename = DownloadInfo.Filename;
            var url = DownloadInfo.Url;
            wc.DownloadFileCompleted += (a, b) =>
            {
                if (b.Error == null)
                {
                    Finished?.Invoke(this, new DownloadFinishedEventArgs()
                    {
                        Success = true,
                        AttemptLeft = AttemptLeft,
                        DownloadInfo = DownloadInfo
                    });
                }
                else
                {
                    AttemptLeft--;
                    if (AttemptLeft == 0)
                    {
                        Finished?.Invoke(this, new DownloadFinishedEventArgs()
                        {
                            Success = false,
                            AttemptLeft = AttemptLeft,
                            DownloadInfo = DownloadInfo
                        });

                        new FileInfo(filename).Delete();
                    }
                    else
                    {
                        wc.DownloadFileAsync(new Uri(url), filename);
                    }
                }
            };

            FileInfo fileInfo = new FileInfo(filename);
            var dire = fileInfo.Directory;
            if (dire.Exists)
            {
                ;
            }
            else
            {
                dire.Create();
            }
            var fi = new FileInfo(filename);
            if (fi.Exists == false)
            {
                wc.DownloadFileAsync(new Uri(url), filename);
            }
            else
            {
                if (fi.Length <= 0)
                {
                    Console.WriteLine($"{filename} 存在但大小为0");
                    wc.DownloadFileAsync(new Uri(url), filename);
                }
                else
                {
                    Console.WriteLine($"{filename} 已经存在");
                    Finished?.Invoke(this, new DownloadFinishedEventArgs()
                    {
                        Success = true,
                        AttemptLeft = AttemptLeft,
                        DownloadInfo = DownloadInfo
                    });
                }
            }
        }
        public event DownloadFinished Finished;

    }

    public delegate void DownloadFinished(object sender, DownloadFinishedEventArgs downloadFinishedEventArgs);
    public class DownloadFinishedEventArgs : EventArgs
    {
        public bool Success { get; set; }
        public int AttemptLeft { get; set; }
        public DownloadInfo DownloadInfo { get; set; }
    }



    public class TaskArgsGenerator
    {
        public TaskArgsGenerator(string urlTemplate, int x, int y, int z)
        {
            MaxX = x;
            MaxY = y;
            Z = z;
            LatestX = 0;
            LatestY = 0;
            UrlTemplate = urlTemplate;
        }

        public int MaxX { get; set; }
        public int MaxY { get; set; }
        public int Z { get; set; }
        public string UrlTemplate { get; set; }

        private int LatestX { get; set; } = 0;
        private int LatestY { get; set; } = 0;

        public bool ShouldGenerateLatestTask()
        {
            if (LatestY >= MaxY) { return false; }
            else
            {
                return true;
            }

        }
        public DownloadInfo GenerateLatestTask()
        {
            var returnInfo = new DownloadInfo()
            {
                Url = string.Format($"{UrlTemplate}", Z, LatestY, LatestX),
                Filename = $"L{Z:D2}/R{LatestY.ToString("X8").ToLower()}/C{LatestX.ToString("X8").ToLower()}.png"
            };

            LatestX++;
            if (LatestX >= MaxX)
            {
                LatestY++;
                LatestX = 0;
            }
            return returnInfo;
        }

    }
    public struct DownloadInfo
    {
        public string Url { get; set; }
        public string Filename { get; set; }
        public string ToString()
        {
            return $"{Url} => {Filename}";
        }
    }
}
